#pragma once

#include <algorithm>
#include <jsoncpp/json/json.h>

#include "index.hpp"
#include "util.hpp"

namespace ns_searcher
{
    // 文档
    struct InvertedElemPrint
    {
        uint64_t doc_id = 0;
        int weight = 0;
        std::vector<std::string> words;
    };

    class Searcher
    {
    private:
        ns_index::Index *index;

    public:
        // 初始化：对某个文件中的内容建立索引
        void InitSearcher(const std::string &input) // 参数：文件名
        {
            index = ns_index::Index::GetInstance();
            std::cout << "获取单例成功" << std::endl;

            index->BuildIndex(input);
            std::cout << "获取正排，倒排索引成功" << std::endl;
        }
        // 搜索：
        void Search(const std::string &query, std::string *json_string) // 参数：搜索的关键字，输出型参数搜索结果
        {
            // 分词：对用户输入进行分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query, &words);

            // 触发：对分词进行搜索
            std::vector<InvertedElemPrint> inverted_list_all;

            std::unordered_map<uint64_t, InvertedElemPrint> token_map;

            for (std::string word : words) // 分出来的所有词
            {
                boost::to_lower(word);
                ns_index::InvertedList *inverted_list = index->GetInvertedList(word);

                if (inverted_list == nullptr)
                {
                    continue;
                }
                // 对该单词对应的倒排拉链中每个 InvertedElem 对象根据 id 入 map进行去重
                for (auto &elem : *inverted_list)
                {
                    // std::cout << "word: " << word << ", id: " << elem.doc_id << std::endl;

                    auto &item = token_map[elem.doc_id];
                    item.doc_id = elem.doc_id;
                    item.weight += elem.weight;
                    item.words.push_back(elem.word);

                    // std::cout << "token_map size: " << token_map.size() << std::endl;
                }
            }

            // map 是【id - 对象】，我们只要一个对象的 vector 不要 id，所以把 seconde 拿出来
            for (auto &item : token_map)
            {
                inverted_list_all.push_back(std::move(item.second));
            }

            // 排序：对搜索结果进行排序
            std::sort(inverted_list_all.begin(), inverted_list_all.end(),
                      [](const InvertedElemPrint &com1, const InvertedElemPrint &com2)
                      {
                          return com1.weight > com2.weight;
                      });

            // json：根据结果，构建 json 串
            Json::Value root;
            for (auto &item : inverted_list_all)
            {
                ns_index::DocInfo *doc = index->GetForwardIndex(item.doc_id);
                if (doc == nullptr)
                {
                    continue;
                }

                Json::Value elem;
                elem["title"] = doc->title;
                elem["desc"] = GetDesc(item.words[0], doc->content);
                elem["url"] = doc->url;
                // elem["weight"] = item.weight; // 不是字符串的，会自动给你转成字符串，这个字段有没有都可以

                root.append(elem);
            }

            std::cout << "tar4" << std::endl;

            Json::StyledWriter writer;
            *json_string = writer.write(root);
        }

    private:
        // 根据关键字，从文档内容中获取一个摘要
        std::string GetDesc(const std::string &keyword, const std::string &html_content)
        {
            // 找到 keyword 所在位置，截取前 50 个字节（不够的话，前面有几个就要几个），后截取 100 个字节（不够，后面有几个就要几个）

            const int prev_step = 50;
            const int next_step = 100;

            // 1. 查找
            auto iter = std::search(html_content.begin(), html_content.end(), keyword.begin(), keyword.end(),
                                    [](int x, int y)
                                    {
                                        return std::tolower(x) == std::tolower(y);
                                    });

            if (iter == html_content.end())
            {
                return "None-not find"; // 这种情况不可能，但防御性处理
            }
            int pos = std::distance(html_content.begin(), iter);

            // 2. 前摘x个，后摘y个
            int start = 0;
            int end = html_content.size() - 1;

            if (pos - prev_step > start) // 或者：pos > start + prev_step
            {
                start = pos - prev_step;
            }
            if (pos + next_step < end)
            {
                end = pos + next_step;
            }
            if (start >= end)
                return "Node- ilegal";

            return html_content.substr(start, end - start + 1);

            // int pos = html_content.find(keyword);
            // if (pos == std::string::npos)
            // {
            //     return "None-not find"; // 这种情况不可能，但防御性处理
            // }

            // 1.  注意，这里返回值其实是一个 size_t 类型，是一个 long unsigned int 类型,但这里不能使用 size_t
            // 但后面有相减和比较的操作，如果相减之后是负数，对于无符号类型来说，就会是一个非常大的数，比较操作就会失真
            // 或者可以将减法变为加法

            // 2. 注意，这里查找是有点问题的，keyword 是全小写的，而find 查找是全匹配查找的，无法忽略大小写查找，所以就会有找不到的情况
            // find 函数无法满足条件，可以使用 search 函数，自己规定匹配方法
        }
    };
}